#include "sync.h"
#include "interrupt.h"
#include "debug.h"

void sema_init(struct semaphore* psema, uint8_t value){
    psema->value = value;
    list_init(&psema->suspend_list);
}

void lock_init(struct lock* plock){
    sema_init(&plock->semaphore, 1);
    plock->holder = NULL;
    plock->holder_repeat_nr = 0;
}

void sema_down(struct semaphore* psema){
    enum intr_status _old_intr_status = intr_disable();
    ASSERT(intr_get_status() == INTR_OFF);
    while(psema->value == 0){
        /* 要求在该信号量的挂起列表中没有该节点 */
        ASSERT(elem_find(&psema->suspend_list, &running_task()->general_tag) == 0);
        /* 将该线程的节点挂载到挂起列表 */
        list_append(&psema->suspend_list, &running_task()->general_tag);
        /* 锁定线程，即设置线程状态并且产生调度 */
        thread_block(TASK_BLOCKED);
    }

    psema->value --;
    ASSERT(psema->value == 0);
    intr_set_status(_old_intr_status);
}
void sema_up(struct semaphore* psema){
    enum intr_status _old_intr_status = intr_disable();
    ASSERT(intr_get_status() == INTR_OFF);
    ASSERT(psema->value == 0);
    /* 如果链表不是空的话 */
    if(!list_empty(&psema->suspend_list)){
        /* 拿出挂起链表中的第一个挂起的线程节点并且将其转换为线程 */
        struct task_struct* __pcb = elem2entry(struct task_struct, general_tag, list_pop(&psema->suspend_list));
        /* 解锁线程 即将线程节点插入到就绪列表首 但不产生调度 等待当前线程时间片执行完毕才调度 */
        thread_unblock(__pcb);
    }

    psema->value++;
    ASSERT(psema->value == 1);

    intr_set_status(_old_intr_status);
}

void lock_acquire(struct lock* plock){
    if(plock->holder != running_task()){
        /* 如果当前线程不是持有锁的线程 则在这里会停留等待（因为lock初始值肯定是1） */
        sema_down(&plock->semaphore);
        plock->holder = running_task();
        ASSERT(plock->holder_repeat_nr == 0);
        plock->holder_repeat_nr = 1;
    }else{
        /* 如果当前线程是持有锁的线程，则计算这个线程获取锁几次 */
        plock->holder_repeat_nr ++;
    }
}

void lock_release(struct lock* plock){
    /* 释放锁必须由持有锁的线程来执行 */
    ASSERT(plock->holder == running_task());
    if(plock->holder_repeat_nr > 1){
        /* 如果获取锁的次数大于1 则先不做任何操作 只对获取锁的次数减1 */
        plock->holder_repeat_nr --;
        return;
    }
    /* 断言 在这里必须等于1 */
    ASSERT(plock->holder_repeat_nr == 1);
    /* 将持有者设置为NULL */
    plock->holder = NULL;
    /* 获取锁次数为0 */
    plock->holder_repeat_nr = 0;
    sema_up(&plock->semaphore);
}